package com.north.light.libbilibilidfm;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;

import androidx.annotation.NonNull;

import com.north.light.libbilibilidfm.base.BiliDanmakuParser;
import com.north.light.libbilibilidfm.base.BiliDanmakuSurfaceView;
import com.north.light.libbilibilidfm.event.DoExamBulletEventListener;
import com.north.light.libbilibilidfm.event.DoExamBulletEventManager;
import com.north.light.libbilibilidfm.utils.DoExamCusBulletCollectionsUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.IDisplayer;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;

/**
 * FileName: DoExamCusBulletView
 * Author: lzt
 * Date: 2022/12/12 15:08
 * 做题弹幕类--继承哔哩哔哩弹幕库
 * 按照进度进行动态添加弹幕
 * change by lzt 20221215 控制值的时候，使用handler进行管理
 * change by lzt 20221229 修改基类为surface view实现的弹幕库
 */
public class DoExamCusBulletViewNew extends BiliDanmakuSurfaceView implements DoExamCusBulletViewNewApi {
    private List<DoExamCusBulletInfo> dataList = new ArrayList<>();
    private DanmakuContext mDanMuContext;
    private BaseDanmakuParser mDanMuParser;
    //播放弹幕handler
    //检查标识
    private static final int HANDLER_CHECK = 0x0001;
    //检查数据间距
    private static final long HANDLER_CHECK_INTERVAL = 200L;
    //弹幕延时出现控制值
    private static final long DELAY_TIME = 1000L;
    //添加数据
    private static final int HANDLER_ADD = 0x0002;
    private static final long HANDLER_ADD_INTERVAL = 50L;
    //恢复or暂停
    private static final int HANDLER_PAUSE_RESUME = 0x0003;

    //外部标识
    private String mIdentify = this.getClass().getSimpleName();

    //当前进度相关-------------------------------------------------------------
    //当前进度
    private long mRelProgress = 0;
    //上一次的进度
    private long mOldRelProgress = 0;
    //进度范围取值
    private long mRelProgressInterval = 1000;
    //上次添加值的数量
    private long addRelProgressLastCount = 1;

    //控件唯一标识
    private String VIEW_TAG = "";


    private int pageType;

    private int adapterPos = -1;

    //弹幕tag key
    private static final int TAG_KEY = 1;

    private static final Object mLockObj = new Object();

    private List<DoExamCusBulletInfo> sendingList = new ArrayList<>();

    //是否需要重新初始化
    private boolean mNeedInit = true;

    private long mTotalProgress = 0;

    private Handler mBulletHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case HANDLER_CHECK:
                    if (!isPrepared() || DoExamCusBulletCollectionsUtils.isEmpty(dataList)) {
                        mBulletHandler.sendEmptyMessageDelayed(HANDLER_CHECK, HANDLER_CHECK_INTERVAL);
                        return;
                    }
                    //判断是否需要清屏
                    boolean clearScreen = (mTotalProgress - 1500 < mRelProgress && mRelProgress < mTotalProgress);
                    if (clearScreen) {
                        clearDanmakusOnScreen();
                        Log.d(TAG, mIdentify + "清屏: ");
                    }

                    //进度不改变，则不会调用，只有改变后，才会调用
                    if (mOldRelProgress != mRelProgress && !clearScreen) {
                        mOldRelProgress = mRelProgress;
                        //检查符合要求的弹幕--往前检查
                        long startProgress = mRelProgress - mRelProgressInterval;
                        long endProgress = mRelProgress;
                        List<DoExamCusBulletInfo> trainResult = new ArrayList<>();

                        //复制对象
                        List<DoExamCusBulletInfo> copyDataList = dataList;

                        for (int i = 0; i < copyDataList.size(); i++) {
                            DoExamCusBulletInfo cacheInfo = copyDataList.get(i);
                            if (cacheInfo.getShowTime() > startProgress && endProgress > cacheInfo.getShowTime()) {
                                if (sendingList.contains(cacheInfo)) {
                                    Log.d(TAG, mIdentify + "检查已存在: " + cacheInfo.getContent());
                                } else {
                                    trainResult.add(cacheInfo);
                                    Log.d(TAG, mIdentify + "检查: " + cacheInfo.getContent());
                                }
                            }
                        }

                        sendingList.clear();
                        if (!DoExamCusBulletCollectionsUtils.isEmpty(trainResult)) {
                            sendingList.addAll(trainResult);
                        }

                        if (!DoExamCusBulletCollectionsUtils.isEmpty(trainResult)) {
                            //排序
                            Collections.sort(trainResult, new Comparator<DoExamCusBulletInfo>() {
                                @Override
                                public int compare(DoExamCusBulletInfo o1, DoExamCusBulletInfo o2) {
                                    return (int) (o1.getShowTime() - o2.getShowTime());
                                }
                            });
                            //计算时间间隔--用于控制出现时机
                            for (int i = 0; i < trainResult.size(); i++) {
                                if (i == 0) {
                                    trainResult.get(i).setBeforeInterval(0);
                                } else {
                                    long interval = trainResult.get(i).getShowTime() - trainResult.get(i - 1).getShowTime();
                                    trainResult.get(i).setBeforeInterval(interval);
                                }
                            }
                            Message message = trainAddMessage(trainResult);
                            mBulletHandler.sendMessageDelayed(message, (addRelProgressLastCount + 1) * HANDLER_ADD_INTERVAL);
                            addRelProgressLastCount = trainResult.size();
                        }
                    }
                    mBulletHandler.sendEmptyMessageDelayed(HANDLER_CHECK, HANDLER_CHECK_INTERVAL);
                    break;
                case HANDLER_ADD:
                    if (msg.obj instanceof HandlerMessage) {
                        HandlerMessage messageObj = (HandlerMessage) msg.obj;
                        List<DoExamCusBulletInfo> bulletList = messageObj.getBulletList();
                        if (!DoExamCusBulletCollectionsUtils.isEmpty(bulletList)) {
                            Log.d(TAG, mIdentify + "添加: " + bulletList.get(0).getContent()
                                    + "时间1：" + bulletList.get(0).getShowTime()
                            );
                            setBullet(bulletList.get(0), false);
                            bulletList.remove(0);
                            if (!DoExamCusBulletCollectionsUtils.isEmpty(bulletList)) {
                                //数据还存在，继续轮询添加
                                Message message = trainAddMessage(bulletList);
                                mBulletHandler.sendMessageDelayed(message, HANDLER_ADD_INTERVAL);
                            }
                        }
                    }
                    break;
                case HANDLER_PAUSE_RESUME:
                    if (msg.obj instanceof Boolean) {
                        boolean pause = (boolean) msg.obj;
                        Log.d(TAG, mIdentify + "暂停恢复--是否暂停: " + pause);
                        if (pause) {
                            pause();
                        } else {
                            resume();
                        }
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };

    private Message trainAddMessage(List<DoExamCusBulletInfo> bulletList) {
        HandlerMessage handlerMessage = new HandlerMessage();
        Message message = mBulletHandler.obtainMessage();
        handlerMessage.setBulletList(bulletList);
        message.obj = handlerMessage;
        message.what = HANDLER_ADD;
        return message;
    }

    private Message trainPauseResumeMessage(boolean pause) {
        Message message = mBulletHandler.obtainMessage();
        message.obj = pause;
        message.what = HANDLER_PAUSE_RESUME;
        return message;
    }

    public DoExamCusBulletViewNew(Context context) {
        super(context);
        initView();
    }

    public DoExamCusBulletViewNew(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DoExamCusBulletViewNew(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    private void initView() {
        VIEW_TAG = getClass().getSimpleName() + System.currentTimeMillis() + Integer.toHexString(hashCode());
        try {
            getHolder().setFormat(PixelFormat.TRANSLUCENT);
            setZOrderOnTop(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 显示一条弹幕
     */
    private void setBullet(DoExamCusBulletInfo bulletInfo, boolean rightNow) {
        if (bulletInfo == null) {
            return;
        }
        BaseDanmaku danMuKu = mDanMuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
        if (danMuKu == null) {
            return;
        }
        try {
            danMuKu.text = bulletInfo.getContent();
            danMuKu.padding = 5;
            //可能会被各种过滤器过滤并隐藏显示
            danMuKu.priority = 1;
            danMuKu.isLive = false;
            if (rightNow) {
                danMuKu.setTime(getCurrentTime() + (new Random().nextInt(10)) + DELAY_TIME);
            } else {
                danMuKu.setTime(getCurrentTime() + bulletInfo.getBeforeInterval() + DELAY_TIME);
            }
            danMuKu.textSize = bulletInfo.getTextSize() * (mDanMuParser.getDisplayer().getDensity() - 0.6f);
            danMuKu.textColor = Color.WHITE;
            danMuKu.textShadowColor = Color.BLACK;
            danMuKu.setTag(TAG_KEY, bulletInfo);
            addDanmaku(danMuKu);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化
     */
    private void init() {
        clean();
        mDanMuParser = BiliDanmakuParser.createParser(null);
        HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();
        //滚动弹幕最大显示3行
        maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 3);
        HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();
        overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, false);
        overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, false);

        mDanMuContext = DanmakuContext.create();

        try {
            Display display = ((Activity) getContext()).getWindow().getWindowManager().getDefaultDisplay();
            float refreshRate = display.getRefreshRate();
            int rate = (int) (1000 / refreshRate);
            mDanMuContext.setFrameUpateRate(rate);
            Log.d(TAG, mIdentify + "rate: " + rate);
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, mIdentify + "rate error");
        }
        mDanMuContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3)
                .setDuplicateMergingEnabled(false)
//                .setScrollSpeedFactor(1.2f)
                .setScaleTextSize(1.2f)
                //绘制背景使用BackgroundCacheStuffer
                .setCacheStuffer(new BackgroundCacheStuffer(), null)
                .setMaximumLines(maxLinesPair)
                .preventOverlapping(overlappingEnablePair)
                .setDanmakuMargin(40);

        prepare(mDanMuParser, mDanMuContext);
        showFPS(false);
        enableDanmakuDrawingCache(true);
        setCallback(new DrawHandler.Callback() {
            @Override
            public void prepared() {
                Log.d(TAG, mIdentify + "prepared");
                start();
                startHandlerCheck();
            }

            @Override
            public void updateTimer(DanmakuTimer timer) {

            }

            @Override
            public void danmakuShown(BaseDanmaku danmaku) {
                Log.d(TAG, mIdentify + "danmakuShown");
            }

            @Override
            public void drawingFinished() {
                Log.d(TAG, mIdentify + "drawingFinished");
            }
        });
    }

    /**
     * 开始检查
     */
    private void startHandlerCheck() {
        mBulletHandler.removeMessages(HANDLER_CHECK);
        mBulletHandler.sendEmptyMessage(HANDLER_CHECK);
    }

    /**
     * 显示
     *
     * @param resetData 是否重置数据
     */
    private void resetData(boolean resetData) {
        try {
            if (!DoExamCusBulletCollectionsUtils.isEmpty(dataList)) {
                //排序
                Collections.sort(dataList, new Comparator<DoExamCusBulletInfo>() {
                    @Override
                    public int compare(DoExamCusBulletInfo o1, DoExamCusBulletInfo o2) {
                        return (int) (o1.getShowTime() - o2.getShowTime());
                    }
                });
                //数据时间去重--关键！！！如果时间重复，数据会出现丢失的情况
                for (int i = 0; i < dataList.size(); i++) {
                    long showTime = dataList.get(i).getShowTime() + i;
                    dataList.get(i).setShowTime(showTime);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initEvent() {
        removeEvent();
        DoExamBulletEventManager.getInstance().setBulletEventListener(VIEW_TAG,
                new DoExamBulletEventListener() {
                    @Override
                    public void play(int adapterPos, int pageType, String url) {
                        if (checkIdentify(String.valueOf(pageType), url, adapterPos)) {
                            if (isPrepared()) {
                                if (mBulletHandler != null) {
                                    Message message = trainPauseResumeMessage(false);
                                    mBulletHandler.sendMessage(message);
                                }
                            }
                        }
                    }

                    @Override
                    public void pause(int adapterPos, int pageType, String url) {
                        if (checkIdentify(String.valueOf(pageType), url, adapterPos)) {
                            if (isPrepared()) {
                                if (mBulletHandler != null) {
                                    Message message = trainPauseResumeMessage(true);
                                    mBulletHandler.sendMessage(message);
                                }
                            }
                        }
                    }

                    @Override
                    public void progress(int adapterPos, int pageType, String url, long progress, long duration) {
                        if (checkIdentify(String.valueOf(pageType), url, adapterPos)) {
                            if (isPrepared()) {
                                seek(progress);
                                setTotal(duration);
                            }
                        }
                    }
                });
    }

    private void removeEvent() {
        DoExamBulletEventManager.getInstance().removeBulletEventListener(VIEW_TAG);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        synchronized (mLockObj) {
            Log.d(TAG, mIdentify + "onAttachedToWindow");
            initEvent();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        synchronized (mLockObj) {
            removeEvent();
            clean();
            release();
            Log.d(TAG, mIdentify + "onDetachedFromWindow" + this.hashCode());
            mNeedInit = true;
        }
    }

    /**
     * 校验控件身份标识是否通过
     */
    private boolean checkIdentify(String remoteType, String removeUrl, int adapterPos) {
        String localIdentify = mIdentify + pageType + this.adapterPos;
        String remoteIdentify = removeUrl + remoteType + adapterPos;
        if (TextUtils.isEmpty(localIdentify) || TextUtils.isEmpty(remoteIdentify)) {
            return false;
        }
        return localIdentify.equals(remoteIdentify);
    }

    //handler自定义内部类
    public static class HandlerMessage implements Serializable {

        private List<DoExamCusBulletInfo> bulletList = new ArrayList<>();

        public List<DoExamCusBulletInfo> getBulletList() {
            return bulletList;
        }

        public void setBulletList(List<DoExamCusBulletInfo> bulletList) {
            this.bulletList = bulletList;
        }
    }

    /**
     * 绘制背景(自定义弹幕样式)
     */
    private static class BackgroundCacheStuffer extends SpannedCacheStuffer {
        // 通过扩展SimpleTextCacheStuffer或SpannedCacheStuffer个性化你的弹幕样式
        final Paint paint = new Paint();

        @Override
        public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {
            danmaku.padding = 10;  // 在背景绘制模式下增加padding
            super.measure(danmaku, paint, fromWorkerThread);
        }

        @Override
        public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {
            //判读当前弹幕是否为自己发的
            if (danmaku.getTag(TAG_KEY) instanceof DoExamCusBulletInfo) {
                DoExamCusBulletInfo doExamCusBulletInfo = (DoExamCusBulletInfo) danmaku.getTag(TAG_KEY);
                if (doExamCusBulletInfo != null && doExamCusBulletInfo.isOwner()) {
                    paint.setColor(Color.WHITE);
                    paint.setStyle(Paint.Style.STROKE);
                    paint.setStrokeWidth(2f);
                    canvas.drawRoundRect(left + 2, top + 2, left + danmaku.paintWidth - 2,
                            top + danmaku.paintHeight - 2, 12, 12, paint);
                }
            }
        }

        @Override
        public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {
            // 禁用描边绘制
        }
    }

    //外部调用------------------------------------------------------------------------------------

    @Override
    public void setData(List<DoExamCusBulletInfo> doExamList) {
        Log.d(TAG, mIdentify + "setData" + this.hashCode());
        if (mNeedInit) {
            mNeedInit = false;
            init();
        }
        clean();
        //数据处理
        dataList.clear();
        dataList.addAll(DoExamCusBulletCollectionsUtils.cloneListSer(doExamList));
        resetData(true);
        startHandlerCheck();
    }

    @Override
    public void addBullet(DoExamCusBulletInfo info, boolean sendRightNow) {
        Log.d(TAG, mIdentify + "addBullet");
        if (info == null) {
            return;
        }
        if (mNeedInit) {
            mNeedInit = false;
            init();
        }
        DoExamCusBulletInfo singleObj = DoExamCusBulletCollectionsUtils.cloneObjectSer(info);
        dataList.add(singleObj);
        resetData(false);
        startHandlerCheck();
        if (sendRightNow) {
            setBullet(singleObj, true);
        }
    }

    @Override
    public void clean() {
        mBulletHandler.removeCallbacksAndMessages(null);
        mRelProgress = 0;
        mOldRelProgress = -1;
        addRelProgressLastCount = 1;
//        dataList.clear();
    }

    /**
     * 毫秒
     */
    @Override
    public void seek(long time) {
        mRelProgress = time;
    }

    private void setTotal(long duration) {
        this.mTotalProgress = duration;
    }

    /**
     * 目前传入的是视频url
     */
    @Override
    public void setIdentify(String key) {
        this.mIdentify = key;
    }

    /**
     * 页面类型
     */
    @Override
    public void setPageType(int pageType) {
        this.pageType = pageType;
    }

    @Override
    public void setAdapterPos(int adapterPos) {
        this.adapterPos = adapterPos;
    }

    @Override
    public void setKey(String identify, int pageType, int adapterPos) {
        setIdentify(identify);
        setPageType(pageType);
        setAdapterPos(adapterPos);
    }

}
